//装饰器
// 装饰器是一种特殊类型的声明，它能够被附加到类声明，方法， 访问符，属性或参数上，来扩展他们的功能；
//通俗来讲装饰器就是一个方法，可以注入到类，方法等里面，拓展他们的功能
// 装饰器使用 @expression这种形式，expression求值后必须为一个函数，它会在运行时被调用，被装饰的声明信息做为参数传入。

// 类装饰器
//定义装饰器
function logClass(params:any) {
  //原型上扩展方法
  params.prototype.apiUrl = ()=>{
    console.log('我是装饰器');
  }
  //扩展属性
  params.prototype.run='我是属性';
}

//普通写法，无法传参
@logClass
class Person {
  constructor(){

  }
}
//实例化
let p:any = new Person();
p.apiUrl();//我是装饰器
console.log(p.run);//我是属性


// 第二种写法，可传参
function myClass(str:string){
  return (params:any)=>{
    params.prototype.massega = str; 
  }
}

@myClass('我是传参的')
class Son {
  constructor(){

  }
}

//实例化
let s:any = new Son();
console.log(s.massega);


//类装饰器不仅能扩展类的方法等，还能重载构造函数，和重写它的属性方法等

function Mylog(params:any) {
  return class extends params {
    //重写属性
    api='我是程凯';
    //重写方法
    sayName(){
      console.log('我是重写方法'+this.api);
    }
  }
}
@Mylog
class Repeat {
  public api:string='我是谁';
  sayName(){
    console.log(this.api);
  }
}

let r = new Repeat();
console.log(r.api);//我是程凯
r.sayName();//我是重写方法我是程凯


// 属性装饰器
/*
属性装饰器会在运行时当做函数被调用，传入以下两个参数,(注意是运行不是实例化)
    1：对于静态成员来说是类的构造函数，对于实例成员是类的原型对象
    2：成员的名字
*/

function myCru(params:string){
  return (target:any,str:string)=>{//这里
    target[str] = params;
  }
}

class Cru{
  @myCru('我是属性装饰器')
  public sex:string|undefined
  sayName(){
    console.log(this.sex);
  }
}
let c =  new Cru();
c.sayName();//我是属性装饰器

//装饰器在这里调用的时候是在运行的时候，而不是在实例化的时候


// 方法装饰器
/*
他会被应用到方法的属性描述符上面，可以修改，监视，修改或者替换方法定义
方法装饰器会在运行时传入下列三个参数
    1：对于静态成员来说是类的构造函数，对于实例成员是类的原型对象
    2：成员的名字
    3：成员的属性描述符
*/
function get(str:string){
  //接受三个参数
  return (target:any,method:any,desc:any)=>{
    //扩展它的一个属性
    target.age = '三笠';
    target.method=()=>{
      console.log('我是扩展那个方法');
    }
    //保存以前的方法
    let oldmet = desc.value;
    //修改以前的方法
    desc.value = function(...args:any[]){
      // 调用以前的方法，传入修改后的参数
      oldmet('我是修改之后的')
    }
  }
}


class Http{
  public name:string|undefined
  @get('我是方法装饰器')
  sayNmae(str:string){
    console.log(str);
  }
}
let h:any = new Http();
console.log(h.age);//三笠
h.method();//我是扩展那个方法
h.sayNmae();//


// 方法参数装饰器
/*
这个表达式会在函数被调用时调用，可以为类的原型增加一些源数据
方法装饰器会在运行时传入下列三个参数
    1：对于静态成员来说是类的构造函数，对于实例成员是类的原型对象
    2：方法的名字
    3：参数在函数参数列表中的索引
*/
function myGet(str:string){
  //接受三个参数
  return (target:any,method:any,index:any)=>{
    console.log(target);
    console.log(method);
    console.log(index);
    target.apiUrl = str;
  }
}
class Mycl {
  apiUrl:string|undefined;
  sayname( @myGet('参数装饰器') utill:any){

  }
}
let m = new Mycl();
console.log(m.apiUrl);//参数装饰器



// 装饰器的执行顺序
function log1(params:string){
  return (target:any)=>{
    console.log(params);
  }
}
function log2(params:string){
  return (target:any)=>{
    console.log(params);
  }
}
function atter1(params:string){
  return (target:any,name:any)=>{
    console.log(params);
  }
}
function mthods(params:string){
  return (target:any,name:any)=>{
    console.log(params);
  }
}

function paramsLog1 (params:string){
  return (target:any,method:any,index:any)=>{
    console.log(params);
    
  }
}
function paramsLog2 (params:string){
  return (target:any,name:any,desc:any)=>{
    console.log(params);
    
  }
}
@log1('我是类装饰器1')
@log2('我是类装饰器2')
class Logs {
  @atter1('-------我是属性装饰器1')
  public apiUrl:string|undefined;
  @mthods('我是方法装饰器')
  getData(){
    return true;
  }
  params(@paramsLog1('我是方法参数装饰器1') ut1:any,@paramsLog2('我是方法参数装饰器2') ut2:any){

  }
}

let l = new Logs();


//执行顺序
// 属性>方法>方法参数>类装饰器
//如果有多个同样的装饰器，他会先执行后面的